package com.coocaa.ops.admin.base.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.coocaa.ops.admin.base.model.MgrErrLogModel;
import com.coocaa.ops.admin.base.util.WebUtil;
import com.coocaa.ops.admin.v1.auth.sys.service.ISysAuthorizeService;
import com.coocaa.ops.common.core.base.controller.BaseController;
import com.coocaa.ops.common.core.base.model.R;
import com.coocaa.ops.common.core.base.model.SimpleSysUserInfo;
import com.coocaa.ops.common.core.biz.auth.entity.SysRunEntity;
import com.coocaa.ops.common.tool.exception.BaseException;
import com.coocaa.ops.common.tool.exception.BusinessException;
import com.coocaa.ops.common.tool.exception.ExceptionUtil;
import com.coocaa.ops.common.tool.exception.IllegalParameterException;
import com.coocaa.ops.common.tool.http.HttpCodeEnums;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.context.request.WebRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author tangminghong
 * @date 2020-06-10 18:36
 */
@Slf4j
public class MgrBaseController extends BaseController {

    @Autowired
    private ISysAuthorizeService sysAuthorizeService;

    @InitBinder
    public void initBinder(WebDataBinder binder, WebRequest request) {
        //转换日期 注意这里的转化要和传进来的字符串的格式一直 如2015-9-9 就应该为yyyy-MM-dd
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // CustomDateEditor为自定义日期编辑器
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
    }

    /**
     * 获取登录用户指定菜单的操作权限
     *
     * @param clzz
     * @param request
     * @return
     */
    protected Map<String, Object> getMenuInfo(Class<?> clzz, HttpServletRequest request) {
        List<SysRunEntity> list = sysAuthorizeService.getMenuTools(clzz, WebUtil.getCurrentUser(request));
        return R.ok(list);
    }

    /**
     * 获取当前用户ID
     *
     * @param request
     * @return
     */
    protected Integer getCurrentUserId(HttpServletRequest request) {
        return WebUtil.getCurrentUserId(request);
    }

    /**
     * 获取当前用户信息-用户ID-用户名称
     *
     * @param request
     * @return
     */
    protected SimpleSysUserInfo getUserInfo(HttpServletRequest request) {
        return WebUtil.getUserInfo(request);
    }


    /**
     * 异常-统一处理
     *
     * @param request
     * @param response
     * @param ex
     * @throws Exception
     */
    @ExceptionHandler({Exception.class})
    @Override
    public void exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {
        MgrErrLogModel errLogModel = this.getWrapperErrMsg(ex, request.getRequestURI());
        String errMsg;
        if (StringUtils.isNotBlank(errLogModel.getDetailMsg())) {
            errMsg = errLogModel.getDetailMsg();
        } else {
            errMsg = errLogModel.getMsg();
        }
        log.error(ExceptionUtil.getErrorPrintLog("访问url:" + errLogModel.getUrl(), errMsg, errLogModel.getOpParams(), ExceptionUtil.getStackTraceAsString(ex, 3)));
        response.setContentType("application/json;charset=UTF-8");
        byte[] bytes = JSON.toJSONBytes(errLogModel, new SerializerFeature[]{SerializerFeature.DisableCircularReferenceDetect});
        response.getOutputStream().write(bytes);
    }

    /**
     * 封装报错信息
     *
     * @param ex
     * @return
     */
    private MgrErrLogModel getWrapperErrMsg(Exception ex, String url) {
        MgrErrLogModel errLogModel = new MgrErrLogModel();
        //设置请求Url
        errLogModel.setUrl(url);
        //设置类名
        errLogModel.setClassName(this.getClassName(ex));
        //设置方法名称
        errLogModel.setMethodName(this.getMethodName(ex));
        if (ex instanceof BusinessException) {
            //处理自定义业务异常
            this.handleBusinessException((BusinessException) ex, errLogModel);
        } else if (ex instanceof BaseException) {
            //处理自定义其他异常
            this.handleBaseException((BaseException) ex, errLogModel);
        } else if (ex instanceof IllegalArgumentException) {
            this.handleIllegalArgumentException((IllegalArgumentException) ex, errLogModel);
        } else if (ex instanceof MybatisPlusException) {
            this.handleMyBatisPlusException((MybatisPlusException) ex, errLogModel);
        } else if(ex instanceof FeignException){
            this.handleFeignException((FeignException) ex, errLogModel);
        }
        else {
            this.handleException(ex, errLogModel);
        }
        errLogModel.setTimestamp(System.currentTimeMillis());
        return errLogModel;
    }

    /**
     * 获取报错类名
     *
     * @param ex
     * @return
     */
    private String getClassName(Exception ex) {
        String className = ex.getStackTrace()[0].getClassName();
        return className;
    }

    /**
     * 获取报错方法名称
     *
     * @param ex
     * @return
     */
    private String getMethodName(Exception ex) {
        String methodName = ex.getStackTrace()[0].getMethodName();
        return methodName;
    }

    /**
     * 批量保存异常
     *
     * @param mybatisPlusException
     * @param errLogModel
     */
    private void handleMyBatisPlusException(MybatisPlusException mybatisPlusException, MgrErrLogModel errLogModel) {
        errLogModel.setCode(HttpCodeEnums.INTERNAL_SERVER_ERROR.value().toString());
        errLogModel.setMsg(mybatisPlusException.getMessage());
        errLogModel.setDetailMsg(ExceptionUtil.getStackTraceAsString(mybatisPlusException.getCause()));
    }


    /**
     * 处理异常-BusinessException
     *
     * @param businessException
     * @param errLogModel
     * @see BusinessException
     */
    private void handleBusinessException(BusinessException businessException, MgrErrLogModel errLogModel) {
        errLogModel.setOpParams(businessException.getErrOpData());
        errLogModel.setCode(businessException.getCode().value().toString());
        errLogModel.setMsg(businessException.getErrMsg());
    }

    /**
     * 处理异常-BaseException
     *
     * @param baseEx
     * @param errLogModel
     * @see BaseException
     */
    private void handleBaseException(BaseException baseEx, MgrErrLogModel errLogModel) {
        this.handleSetCodeAndMsg(baseEx, errLogModel);
    }

    /**
     * 处理异常-IllegalArgumentException
     *
     * @param ex
     * @param errLogModel
     * @see IllegalParameterException ,
     * @see IllegalArgumentException
     */
    private void handleIllegalArgumentException(IllegalArgumentException ex, MgrErrLogModel errLogModel) {
        IllegalParameterException parameterException = new IllegalParameterException(ex.getMessage());
        this.handleSetCodeAndMsg(parameterException, errLogModel);
    }

    /**
     * @param baseEx
     * @param errLogModel
     */
    private void handleSetCodeAndMsg(BaseException baseEx, MgrErrLogModel errLogModel) {
        errLogModel.setCode(baseEx.getCode().value().toString());
        String msg = baseEx.getMessage();
        errLogModel.setMsg(StringUtils.isNotBlank(msg) ? msg : baseEx.getCode().msg());
    }

    /**
     * 处理异常-Exception
     *
     * @param ex
     * @param errLogModel
     * @see Exception
     */
    private void handleFeignException(FeignException ex, MgrErrLogModel errLogModel) {
        errLogModel.setCode(HttpCodeEnums.INTERNAL_SERVER_ERROR.value().toString());
        errLogModel.setMsg(R.ERR_MSG);
        //设置详情报错信息
        String trimErrMsg = ExceptionUtil.getStackTraceAsString(ex);
        errLogModel.setDetailMsg(trimErrMsg);
    }

    private void handleException(Throwable ex, MgrErrLogModel errLogModel) {
        errLogModel.setCode(HttpCodeEnums.INTERNAL_SERVER_ERROR.value().toString());
        String msg = StringUtils.defaultIfBlank(ex.getMessage(), HttpCodeEnums.INTERNAL_SERVER_ERROR.msg());
        boolean isMsgTooLarge = isMsgTooLarge(ex.getMessage());
        errLogModel.setMsg(isMsgTooLarge ? R.ERR_MSG : msg);
        if (isMsgTooLarge) {
            //设置详情报错信息
            String trimErrMsg = ExceptionUtil.getStackTraceAsString(ex);
            errLogModel.setDetailMsg(trimErrMsg);
        }
    }

    private boolean isMsgTooLarge(String message) {
        return message == null || message.length() > 100;

    }
}
